/*
 * Socket.cpp
 *
 *  Created on: 2017年8月1日
 *      Author: heqian
 */

#include "Socket.h"
#include <netinet/in.h>    // for sockaddr_in
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>

namespace utils {

    Socket::Socket() :
            mSocket(-1), mType(Socket::NONE) {
        mError = "";
    }

    Socket::Socket(Socket *socket) {
        mSocket = socket->mSocket;
        mType = socket->mType;
        mError = socket->mError;
        memcpy(&mRfds, &socket->mRfds, sizeof(fd_set));
    }

    Socket::~Socket() {
        Close();
    }

    void Socket::Close() {
        if (mSocket > -1) {
            close(mSocket);
            mSocket = -1;
            mType = Socket::NONE;
        }
    }

    std::string Socket::getError() {
        return mError;
    }

    Socket::Type Socket::getType() {
        return mType;
    }

    bool Socket::init(int port) {
        if (mType != Socket::NONE) {
            mError = "Socket not Empty!";
            return false;
        }

        sockaddr_in me_addr;

        memset(&me_addr, 0, sizeof(sockaddr_in));

        me_addr.sin_family = AF_INET;
        me_addr.sin_addr.s_addr = htons(INADDR_ANY); //INADDR_ANY表示自动获取本机地址
        me_addr.sin_port = htons(port);    //0表示让系统自动分配一个空闲端口

        mSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (mSocket < 0) {
            mError = "Create Socket Failed!";
            return false;
        }

        if (bind(mSocket, (struct sockaddr *) &me_addr, sizeof(me_addr))) {
            mError = "Bind Port Failed!";
            Close();
            return false;
        }
        return true;
    }

    bool Socket::Connect(std::string url, int port) {
        if (!init()) {
            return false;
        }

        mType = Socket::CILENT;

        struct sockaddr_in target_addr;

        memset(&target_addr, 0, sizeof(target_addr));
        target_addr.sin_family = AF_INET;
        target_addr.sin_port = htons(port);

        if (inet_aton(url.c_str(), &target_addr.sin_addr) == 0) {
            struct hostent *pURL = gethostbyname(url.c_str());
            if (pURL == nullptr) {
                mError = "Server url Address Error!";
                Close();
                return false;
            }
            target_addr.sin_addr.s_addr = *((unsigned long *) pURL->h_addr_list[0]);
        }

        if (connect(mSocket, (struct sockaddr *) &target_addr, sizeof(target_addr)) < 0) {
            mError = "Can Not Connect To " + url;
            Close();
            return false;
        }
        return true;

    }

    bool Socket::Server(int port, int backlog) {
        if (!init(port)) {
            return false;
        }

        mType = Socket::SERVER;
        if (listen(mSocket, backlog)) {
            mError = "Server Listen Failed!";
            Close();
            return false;
        }
        return true;
    }

    Prt<Socket> Socket::Accept() {
        Prt<Socket> ret;
        if (mType != Socket::SERVER) {
            mError = "Socket not is server!";
            return ret;
        }

        struct sockaddr_in client_addr;
        socklen_t length = sizeof(client_addr);

        int new_server_socket = accept(mSocket, (struct sockaddr *) &client_addr, &length);
        if (new_server_socket < 0) {
            mError = "Server Accept Failed!";
            return ret;
        }

        ret = Prt<Socket>(new Socket());
        ret->mSocket = new_server_socket;
        ret->mType = Socket::CILENT;
        return ret;
    }

    int Socket::Send(char *data, int length) {
        if (mSocket != 0) {
            return send(mSocket, data, length, 0);
        }
        mError = "Socket disconnect!!";
        return -1;
    }

    int Socket::Recv(char *data, int length) {
        if (mSocket != 0) {
            return recv(mSocket, data, length, 0);
        }
        mError = "Socket disconnect!!";
        return -1;
    }

    int Socket::Select(float time) {
        if (mSocket > -1) {
            struct timeval tv;
            fd_set exceptfds;
            FD_ZERO(&exceptfds);
            FD_ZERO(&mRfds);/* 把集合清空 */
            FD_SET(mSocket, &mRfds); /* 把当前连接句柄new_fd加入到集合中 */

            tv.tv_sec = (int) time; /* 设置最大等待时间 */
            tv.tv_usec = (time - tv.tv_sec) * 1000000;
            int ret = select(mSocket + 1, &mRfds, NULL, &exceptfds, &tv);/* 开始等待 */
            if (ret != 0) {
                int a = exceptfds.fds_bits[0];
            }
            return ret;
        } else {
            mError = "Socket disconnect!!";
            return -1;
        }
    }

    int Socket::getPort() {
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(sockaddr_in));

        if (mSocket != 0) {
            unsigned int addrlen = sizeof(sockaddr_in);
           // getsockname(mSocket, (struct sockaddr *) &addr, &addrlen);
        }
        return ntohs(addr.sin_port);
    }

} /* namespace qq */
